/* biogeo.c is part of VIP program
 * Written by J. Salvador Arias, 2010
 *      CONICET, INSUE, Instituto Miguel Lillo,
 *      Miguel Lillo 205, S.M. de Tucuman (4000)
 *      Tucuman, Argentina
 *      e-mail: jsalarias@csnat.unt.edu.ar
 *
 * VIP is a free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation and
 * Creative Commons.
 *
 * This file and the VIP program is distributed in the hope
 * that it will be useful, but WITHOUT ANY WARRANTY; without
 * even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * To view a copy of this license, visit
 * http://creativecommons.org/licenses/GPL/2.0/
 * or send a letter to
 * Creative Commons, 171 Second Street, Suite 300,
 * San Francisco, California, 94105, USA.
 */

#include "biogeo.h"
#include "bitfield.h"
#include "tessellation.h"
#include <glib-object.h>

void StartBioGeo () {
 trees = NULL;
 lastTree = NULL;
 numTrees = 0;

 spp = NULL;
 lastSpp = NULL;
 numSpp = 0;

 geoRefs = NULL;
 lastGeoRef = NULL;
 numGeoRefs = 0;

 maxAzimuth = -1.00;
 minAzimuth = 361.00;
 maxInclination = -1.00;
 minInclination = 181.00;
 worldStart = -180.000000;

 params . costNoVic = 1;
 params . costRemov = 1;
 params . costPartRemov = 1;
 params . costTimeLag = 0;
 params . extra = 0;
 params . overlap = 0;
 params . timeLag = 0;
 params . timeDiff = 0;
 params . mode = USE_INTS;
 params . search = NORMAL_SEARCH;
 params . algorithm = PAGE_SEARCH | FLIP_NODES | FIRST_SECTOR;
 params . its = 100;
 params . hold = 10;
 params . maxHold = 100;
 params . sectSize = 20;
 params . generations = 1000;
}

void DestroyBioGeo () {
 GList* navTree;

 if (trees != NULL) {
   DestroyRecs ();
   for (navTree = trees; navTree != NULL; navTree = g_list_next (navTree))
     DestroyTree ((PHYLOTREE*) navTree -> data);
   g_list_free (trees);
 }
 DestroyTermList (spp);
 DestroyGeoList (geoRefs);
 StartBioGeo ();
}

void DestroyTree (PHYLOTREE* tree) {
 GNode* actNode;
 GNode* nuAct;
 PHYLONODE* node;

 if (tree == NULL) return;
 g_ptr_array_free (tree -> nodeArray, TRUE);
 actNode = tree -> root;
 /* Deletes the tree without recursive function callings */
 while (actNode != NULL) {
   if (g_node_first_child (actNode) != NULL) {
     actNode = g_node_last_child (actNode);
     continue;
   }
   if (g_node_prev_sibling (actNode) != NULL)
     nuAct = g_node_prev_sibling (actNode);
   else
     nuAct = actNode -> parent;
   g_node_unlink (actNode);
   node = actNode -> data;
   if (node -> layout != NULL)
     g_object_unref (node -> layout);
   g_free (node -> name);
   g_slice_free1 (sizeof (PHYLONODE), node);
   g_node_destroy (actNode);
   actNode = nuAct;
 }
 g_free (tree -> name);
 g_slice_free1 (sizeof (PHYLOTREE), tree);
}

PHYLOTREE* CreateTree (gchar* name, SCOUNTER id) {
 PHYLOTREE* nu;

 nu = g_slice_alloc (sizeof (PHYLOTREE));
 if (name == NULL)
   nu -> name = g_strdup_printf ("unnamed tree %ld", id);
 else
   nu -> name = g_strdup ("unnamed tree");
 nu -> id = id;
 nu -> root = NULL;
 nu -> list = NULL;
 nu -> nodeArray = g_ptr_array_new ();
 nu -> recOR = NULL;
 nu -> recCons = NULL;
 nu -> recs = nu -> actRec = NULL;
 nu -> stored = 0;
 nu -> suppNodes = 0;
 nu -> maxTreeAge = NO_AGE;
 nu -> x = 0;
 nu -> y = 0;
 return nu;
}

PHYLOTREE* GetTreeFromID (SCOUNTER id) {
 GList* navTree;
 PHYLOTREE* toRet;

 if (trees == NULL) return NULL;
 for (navTree = trees; navTree != NULL; navTree = g_list_next (navTree)) {
   toRet = (PHYLOTREE*) navTree -> data;
   if (toRet -> id == id) return toRet;
 }
 return NULL;
}

GNode* CreateNode (PHYLOTREE* tree, GNode* anc, GNode* lastSibling, SCOUNTER id) {
 PHYLONODE* nu;
 GNode* toRet;

 nu = g_slice_alloc (sizeof (PHYLONODE));
 nu -> name = NULL;
 nu -> term = NULL;
 nu -> tree = tree;
 nu -> withData = 0;
 nu -> fill = 30000;
 nu -> numTerms = 0;
 nu -> level = 0;
 nu -> nested = 0;
 nu -> minAge = NO_AGE;
 nu -> constrained = FALSE;
 nu -> ageX = -1;
 nu -> minX = -1;
 nu -> maxX = -1;
 nu -> y = -1;
 nu -> startY = -1;
 nu -> endY = -1;
 nu -> size = 0;
 nu -> alloSet = NULL;
 toRet = g_node_new (nu);
 nu -> node = toRet;
 nu -> layout = NULL;
 AddChildren (toRet, anc, lastSibling);
 nu -> id = id;
 nu -> rec = NULL;
 return toRet;
}

void RemoveAlloSets () {
 GList* navTree;
 PHYLOTREE* actTree;
 GNode* node;
 PHYLONODE* actNode;
 SCOUNTER i;

 if (trees != NULL) {
   for (navTree = trees; navTree != NULL; navTree = g_list_next (navTree)) {
     actTree = (PHYLOTREE*) navTree -> data;
     for (i = 0; i < actTree -> nodeArray -> len; ++ i) {
       node = (GNode*) g_ptr_array_index (actTree -> nodeArray, i);
       actNode = (PHYLONODE*) node -> data;
       if (actNode -> alloSet != NULL) {
         DestroyAlloSet (actNode -> alloSet);
         g_slice_free1 (sizeof (ALLOPATRY), actNode -> alloSet);
         actNode -> alloSet = NULL;
       }
     }
   }
 }
}

void PrepareTree (PHYLOTREE* tree) {
 GNode* navNode;
 GNode* navAnc;
 PHYLONODE* actNode;
 PHYLONODE* ancNode;
 SCOUNTER y;
 SCOUNTER maxX;

 y = 0;
 actNode = (PHYLONODE*) tree -> root -> data;
 maxX = actNode -> level - 1;
 for (navNode = tree -> root; navNode != NULL; ) {
   actNode = (PHYLONODE*) navNode -> data;
   if ((actNode -> startY == -1) && (g_node_first_child (navNode) != NULL)) {
     navNode = g_node_first_child (navNode);
     continue;
   }
   actNode -> minX = actNode -> nested;
   if ((tree -> maxTreeAge > 0) && (tree -> maxTreeAge != NO_AGE)) {
     if (actNode -> minAge != NO_AGE)
       actNode -> ageX = tree -> maxTreeAge - actNode -> minAge;
     else
       actNode -> ageX = tree -> maxTreeAge;
   }
   if (g_node_first_child (navNode) == NULL) {
     actNode -> maxX = maxX;
     actNode -> y = actNode -> startY = actNode -> endY = y;
     y += 2;
     for (navAnc = navNode -> parent; navAnc != NULL; navAnc = navAnc -> parent) {
       ancNode = (PHYLONODE*) navAnc -> data;
     }
   }
   else {
     actNode -> maxX = maxX - (actNode -> level - 1);
     actNode -> y = actNode -> startY + ((actNode -> endY - actNode -> startY) / 2);
   }
   if (navNode -> parent != NULL) {
     ancNode = (PHYLONODE*) navNode -> parent -> data;
     if (ancNode -> startY == -1)
       ancNode -> startY = actNode -> y;
     ancNode -> endY = actNode -> y;
   }
   if (g_node_next_sibling (navNode) == NULL)
     navNode = navNode -> parent;
   else
     navNode = g_node_next_sibling (navNode);
   continue;
 }
}

void SortNodes (SFLAG mode) {
 GList* navTree;
 PHYLOTREE* tree;
 PHYLONODE* actNode;
 PHYLONODE* descNode;
 PHYLONODE* sibNode;
 GNode* node;
 GNode* desc;
 GNode* sib;
 GNode* tmp;
 SCOUNTER i;
 SFLAG swap;
 SFLAG change;

 for (navTree = GETTREES; navTree != NULL; navTree = g_list_next (navTree)) {
   tree = (PHYLOTREE*) navTree -> data;
   change = FALSE;
   for (i = 0; i < tree -> nodeArray -> len; ++ i) {
     node = (GNode*) g_ptr_array_index (tree -> nodeArray, i);
     actNode = node -> data;
     if (g_node_first_child (node) == NULL) continue;
     for (desc = g_node_first_child (node); ; desc = g_node_next_sibling (desc)) {
       sib = g_node_next_sibling (desc);
       if (sib == NULL) break;
       descNode = (PHYLONODE*) desc -> data;
       for (; sib != NULL; sib = g_node_next_sibling (sib)) {
         swap = FALSE;
         sibNode = (PHYLONODE*) sib -> data;
         switch (mode) {
          case TREE_TERMS_UP: if (sibNode -> numTerms < descNode -> numTerms)
                 swap = TRUE;
               break;
          case TREE_TERMS_DOWN: if (sibNode -> numTerms > descNode -> numTerms)
                 swap = TRUE;
               break;
          case TREE_TERMS_MIX: if (((actNode -> nested % 2) == 0) && (sibNode -> numTerms < descNode -> numTerms))
                 swap = TRUE;
               else if (((actNode -> nested % 2) != 0) && (sibNode -> numTerms > descNode -> numTerms))
                 swap = TRUE;
               break;
          case TREE_TERMS_DEF: if (sibNode -> id < descNode -> id)
                 swap = TRUE;
               break;
         }
         if (swap == TRUE) {
           if (desc -> next == sib) {
             desc -> next = sib -> next;
             sib -> prev = desc -> prev;
             desc -> prev = sib;
             sib -> next = desc;
             if (sib -> prev != NULL)
               sib -> prev -> next = sib;
             if (desc -> next != NULL)
               desc -> next -> prev = desc;
           }
           else {
             if (desc -> prev != NULL)
               desc -> prev -> next = sib;
             desc -> next -> prev = sib;
             if (sib -> next != NULL)
               sib -> next -> prev = desc;
             sib -> prev -> next = desc;
             tmp = desc -> prev;
             desc -> prev = sib -> prev;
             sib -> prev = tmp;
             tmp = desc -> next;
             desc -> next = sib -> next;
             sib -> next = tmp;
           }
           if (desc == g_node_first_child (node))
             node -> children = sib;
           descNode = sibNode;
           tmp = desc;
           desc = sib;
           sib = tmp;
           change = TRUE;
         }
       }
     }
   }
   if (change == TRUE) {
     for (i = 0; i < tree -> nodeArray -> len; ++ i) {
       node = (GNode*) g_ptr_array_index (tree -> nodeArray, i);
       actNode = (PHYLONODE*) node -> data;
       actNode -> startY = -1;
     }
     PrepareTree (tree);
   }
 }
}

PHYLONODE* GetNodeFromID (PHYLOTREE* tree, SCOUNTER id) {
 SCOUNTER i;
 GNode* node;
 PHYLONODE* actNode;

 if (tree == NULL) return NULL;
 for (i = 0; i < tree -> nodeArray -> len; ++ i) {
   node = (GNode*) g_ptr_array_index (tree -> nodeArray, i);
   actNode = (PHYLONODE*) node -> data;
   if (actNode -> id == id) return actNode;
 }
 return NULL;
}

TERMINAL* CreateTerminal (GNode* node) {
 TERMINAL* nuTerm;

 nuTerm = g_slice_alloc (sizeof (TERMINAL));
 nuTerm -> name = NULL;
 nuTerm -> node = node;
 nuTerm -> list = NULL;
 nuTerm -> firstGeo = NULL;
 nuTerm -> numGeo = 0;
 nuTerm -> obs = NULL;
 nuTerm -> synonyms = NULL;
 return nuTerm;
}

void DestroyTermList (GList* list) {
 GList* nav;
 GList* navSyn;
 TERMINAL* term;

 if (list == NULL) return;
 if (g_list_previous (list) != NULL)
   g_list_previous (list) -> next = NULL;
 for (nav = list; nav != NULL; nav = g_list_next (nav)) {
   term = (TERMINAL*) nav -> data;
   if (term -> obs != NULL)
     g_slice_free1 (GETBITFIELDSIZE, term -> obs);
   if (term -> synonyms != NULL) {
     for (navSyn = term -> synonyms; navSyn != NULL; navSyn = g_list_next (navSyn))
       g_free (navSyn -> data);
     g_list_free (term -> synonyms);
   }
   g_slice_free1 (sizeof (TERMINAL), term);
 }
 g_list_free (list);
}

TERMINAL* GetTermFromName (gchar* name) {
 GList* nav;
 GList* navSyn;
 TERMINAL* term;
 gchar* syn;

 for (nav = spp; nav != NULL; nav = g_list_next (nav)) {
   term = (TERMINAL*) nav -> data;
   if (term -> name != NULL) {
     if (!g_strcmp0 (name, term -> name)) return term;
   }
 }
 for (nav = spp; nav != NULL; nav = g_list_next (nav)) {
   term = (TERMINAL*) nav -> data;
   for (navSyn = term -> synonyms; navSyn != NULL; navSyn = g_list_next (navSyn)) {
     syn = (gchar*) navSyn -> data;
     if (!g_strcmp0 (name, syn)) return term;
   }
 }
 return NULL;
}

gchar* AddSynonim (TERMINAL* actTerm, gchar* syn) {
 GList* nav;
 GList* nuSyn;
 gchar* toAdd;
 gchar* probe;
 SINTEGER cmp;

 if (actTerm -> synonyms == NULL) {
   toAdd = g_strdup (syn);
   nuSyn = InsertListElement (NULL, toAdd);
   actTerm -> synonyms = nuSyn;
   return toAdd;
 }
 toAdd = NULL;
 for (nav = actTerm -> synonyms; nav != NULL; nav = g_list_next (nav)) {
   probe = (gchar*) nav -> data;
   cmp = g_strcmp0 (syn, probe);
   if (cmp == 0) return probe;
   else if (cmp < 0) {
     toAdd = g_strdup (syn);
     if (nav == actTerm -> synonyms) {
       nuSyn = InsertListElement (NULL, toAdd);
       nuSyn -> next = actTerm -> synonyms;
       actTerm -> synonyms -> prev = nuSyn;
       actTerm -> synonyms = nuSyn;
       break;
     }
     InsertListElement (g_list_previous (nav), toAdd);
     break;
   }
   else if (g_list_next (nav) == NULL) {
     toAdd = g_strdup (syn);
     InsertListElement (nav, toAdd);
     break;
   }
 }
 return toAdd;
}

GList* CreateGeoRef (GList* last, GList* term, SREAL lon, SREAL lat) {
 GList* toRet;
 GEOREF* nuGeo;

 nuGeo = g_slice_alloc (sizeof (GEOREF));
 nuGeo -> lon = lon;
 nuGeo -> lat = lat;
 nuGeo -> actCell = -1;
 nuGeo -> azimuth = FROMLONTOAZM (lon);
 nuGeo -> inclination = FROMLATTOINC (lat);
 nuGeo -> sp = term;
 nuGeo -> actX = -1;
 nuGeo -> actY = -1;
 toRet = InsertListElement (last, nuGeo);
 nuGeo -> list = toRet;
 nuGeo -> name = NULL;
 nuGeo -> collec = NULL;
 nuGeo -> catalog = NULL;
 nuGeo -> url = NULL;
 nuGeo -> reference = NULL;
 nuGeo -> identifier = NULL;
 nuGeo -> sampleDate = NULL;
 nuGeo -> collector = NULL;
 return toRet;
}

void DestroyGeoList (GList* toDel) {
 GList* navGeo;
 GEOREF* actGeo;

 if (toDel == NULL) return;
 if (g_list_previous (toDel) != NULL)
   g_list_previous (toDel) -> next = NULL;
 for (navGeo = toDel; navGeo != NULL; navGeo = g_list_next (navGeo)) {
   actGeo = (GEOREF*) navGeo -> data;
   if (actGeo -> collec != NULL)
     g_free (actGeo -> collec);
   if (actGeo -> catalog != NULL)
     g_free (actGeo -> catalog);
   if (actGeo -> url != NULL)
     g_free (actGeo -> url);
   if (actGeo -> reference != NULL)
     g_free (actGeo -> reference);
   if (actGeo -> identifier != NULL)
     g_free (actGeo -> identifier);
   if (actGeo -> sampleDate != NULL)
     g_free (actGeo -> sampleDate);
 }
 DestroyList (toDel, sizeof (GEOREF));
}

RECONS* CreateRecons (PHYLONODE* node) {
 RECONS* toRet;

 toRet = (RECONS*) g_slice_alloc (sizeof (RECONS));
 toRet -> node = node;
 toRet -> cells = CreateBitField ();
 toRet -> obs = CreateBitField ();
 toRet -> setA = toRet -> setB = toRet -> removSet = NULL;
 toRet -> numVics = toRet -> numNoVics = toRet -> numRemovNodes = 0;
 toRet -> cost = 0;
 toRet -> numCells = toRet -> numObs = 0;
 toRet -> overlap = 0;
 toRet -> active = 0;
 toRet -> timeLag = 0;
 toRet -> maxAge = NO_AGE;
 toRet -> nodeFlag = 0;
 toRet -> triangles = NULL;
 return toRet;
}

void DestroySingleRec (GPtrArray* recArray) {
 SCOUNTER i;
 RECONS* actRec;
 PHYLONODE* node;

 for (i = 0; i < recArray -> len; ++ i) {
   actRec = (RECONS*) g_ptr_array_index (recArray, i);
   node = actRec -> node;
   if (node -> rec == actRec)
     node -> rec = NULL;
   if (actRec -> triangles != NULL)
     DestroyTriangleList (actRec -> triangles);
   g_slice_free1 (GETBITFIELDSIZE, actRec -> cells);
   g_slice_free1 (GETBITFIELDSIZE, actRec -> obs);
   g_slice_free1 (sizeof (RECONS), actRec);
 }
 g_ptr_array_free (recArray, TRUE);
}

void DestroyRecs () {
 GList* navTree;
 PHYLOTREE* tree;

 for (navTree = trees; navTree != NULL; navTree = g_list_next (navTree)) {
   tree = (PHYLOTREE*) navTree -> data;
   if (tree -> recOR != NULL) {
     DestroySingleRec (tree -> recOR);
     tree -> recOR = NULL;
   }
   if (tree -> recs != NULL) {
     DestroyRecList (tree -> recs);
     tree -> recs = NULL;
   }
   if (tree -> recCons != NULL) {
     DestroySingleRec (tree -> recCons);
     tree -> recCons = NULL;
   }
   tree -> actRec = NULL;
   tree -> stored = 0;
 }
}

void DestroyRecList (GList* recList) {
 GList* navRec;

 if (recList == NULL) return;
 for (navRec = recList; navRec != NULL; navRec = g_list_next (navRec)) {
   if (navRec -> data == NULL) continue;
   DestroySingleRec ((GPtrArray*) navRec -> data);
 }
 g_list_free (recList);
}

void SetTreeBestScore (PHYLOTREE* tree) {
 RECONS* rec;
 SREAL best;

 if (tree -> recOR == NULL) return;
 rec = (RECONS*) g_ptr_array_index (tree -> recOR, 0);
 best = 2 * rec -> cost;
 if (tree -> recs != NULL)
   best = GetBestScore (tree -> recs);
 if (best > rec -> cost)
   best = rec -> cost;
 tree -> best = best;
}

SREAL GetBestScore (GList* recs) {
 GList* navRec;
 GPtrArray* actRec;
 RECONS* rec;
 SREAL best;

 actRec = (GPtrArray*) recs -> data;
 rec = (RECONS*) g_ptr_array_index (actRec, 0);
 best = rec -> cost;
 for (navRec = g_list_next (recs); navRec != NULL; navRec = g_list_next (navRec)) {
   actRec = (GPtrArray*) navRec -> data;
   rec = (RECONS*) g_ptr_array_index (actRec, 0);
   if (best > rec -> cost)
     best = rec -> cost;
 }
 return best;
}

SCOUNTER FilterRecList (GList** recs, SREAL bound, SREAL extra) {
 RECONS* actRec;
 SREAL best;
 GList* navRec;
 GList* nextRec;
 GPtrArray* tmpRec;

 if (*recs == NULL) return 0;
 /* Search for the best reconstruction */
 best = GetBestScore (*recs);
 if ((bound > 0) && (best > (bound + extra))) {
   /* All recosntructions are worst than actual bound */
   DestroyRecList (*recs);
   *recs = NULL;
   return bound;
 }
 for (navRec = *recs; navRec != NULL; navRec = nextRec) {
   nextRec = g_list_next (navRec);
   tmpRec = (GPtrArray*) navRec -> data;
   actRec = (RECONS*) g_ptr_array_index (tmpRec, 0);
   if (actRec -> cost > (best + extra)) {
     if (navRec == *recs)
       *recs = nextRec;
     DestroySingleRec (tmpRec);
     DestroyLink (navRec);
   }
 }
 return best;
}

void FilterRecs (SREAL extra) {
 GList* navTree;
 PHYLOTREE* tree;
 SREAL best;
 RECONS* actRec;

 for (navTree = GETTREES; navTree != NULL; navTree = g_list_next (navTree)) {
   tree = (PHYLOTREE*) navTree -> data;
   actRec = (RECONS*) g_ptr_array_index (tree -> recOR, 0);
   best = actRec -> cost;
   if (tree -> recs != NULL)
     best = FilterRecList (&tree -> recs, best, extra);
   g_string_append_printf (outLog,"\n\nTree %ld (%s)\nBest score: %0.4f", tree -> id, tree -> name, best);
   if (tree -> recs != NULL) {
     if (extra > 0)
       g_string_append_printf (outLog, "\nKeeping reconstructions between %.4f-%.4f", best, best + extra);
     else
       g_string_append_printf (outLog, "\nKeeping best reconstructions");
     tree -> stored = g_list_length (tree -> recs);
     g_string_append_printf (outLog, "\n%ld reconstructions keeped", tree -> stored);
   }
   else {
     tree -> stored = 0;
     g_string_append_printf (outLog, "\nJust keeping OR reconstruction");
   }
 }
}

GEOREF* AddGeoRef (SREAL lon, SREAL lat, GNode* actNode) {
 PHYLONODE* node;
 TERMINAL* term;
 GEOREF* geo;
 GList* nuGeo;

 node = (PHYLONODE*) actNode -> data;
 term = node -> term;
 if (term -> numGeo == 0) {
   nuGeo = CreateGeoRef (GETLASTGEOREF, term -> list, lon, lat);
   term -> firstGeo = nuGeo;
   if (GETGEOREF == NULL)
     SETGEOREF (nuGeo);
   SETLASTGEOREF (nuGeo);
 }
 else {
   nuGeo = CreateGeoRef (term -> firstGeo, term -> list, lon, lat);
   if (GETLASTGEOREF == term -> firstGeo)
     SETLASTGEOREF (nuGeo);
 }
 INCREASENUMGEOREFS (1);
 ++ term -> numGeo;
 geo = (GEOREF*) nuGeo -> data;
 UpdateLimits (geo);
 UpdateWithData (term);
 return geo;
}

void DeleteGeoRef (GEOREF* toDel) {
 GList* actGeo;
 TERMINAL* term;

 actGeo = toDel -> list;
 if (GETLASTGEOREF == actGeo)
   SETLASTGEOREF (g_list_previous (actGeo));
 if (GETGEOREF == actGeo)
   SETGEOREF (g_list_next (actGeo));
 term = (TERMINAL*) toDel -> sp -> data;
 if (term -> firstGeo == actGeo) {
   if (term -> numGeo > 1)
     term -> firstGeo = g_list_next (actGeo);
   else
     term -> firstGeo = NULL;
 }
 -- term -> numGeo;
 DECREASENUMGEOREFS (1);
 if (toDel -> collec != NULL)
   g_free (toDel -> collec);
 if (toDel -> catalog != NULL)
   g_free (toDel -> catalog);
 if (toDel -> url != NULL)
   g_free (toDel -> url);
 if (toDel -> reference != NULL)
   g_free (toDel -> reference);
 if (toDel -> identifier != NULL)
   g_free (toDel -> identifier);
 if (toDel -> sampleDate != NULL)
   g_free (toDel -> sampleDate);
 DestroyLinkElement (actGeo, sizeof (GEOREF));
 UpdateWithData (term);
}

void UpdateLimits (GEOREF* geo) {
 if (GETMAXAZM < geo -> azimuth)
   SETMAXAZM (geo -> azimuth);
 if (GETMINAZM > geo -> azimuth)
   SETMINAZM (geo -> azimuth);
 if (GETMAXINC < geo -> inclination)
   SETMAXINC (geo -> inclination);
 if (GETMININC > geo -> inclination)
   SETMININC (geo -> inclination);
}

SFLAG SetNodeAge (GNode* node, SCOUNTER age, SFLAG changeAll) {
 GNode* navNode;
 PHYLONODE* actNode;
 PHYLONODE* actNav;
 SCOUNTER i;
 PHYLOTREE* tree;

 actNode = (PHYLONODE*) node -> data;
 tree = actNode -> tree;
 if ((age < actNode -> minAge) && (g_node_first_child (node) != NULL) && (actNode -> minAge != NO_AGE)) return FALSE;
 actNode -> minAge = age;
 actNode -> ageX = tree -> maxTreeAge - actNode -> minAge;
 for (navNode = node -> parent; navNode != NULL; navNode = navNode -> parent) {
   actNav = (PHYLONODE*) navNode -> data;
   if ((actNav -> minAge < actNode -> minAge) || (actNav -> minAge == NO_AGE)) {
     actNav -> minAge = actNode -> minAge;
     actNav -> ageX = tree -> maxTreeAge - actNav -> minAge;
   }
   else break;
   actNode = actNav;
 }
 if ((age != NO_AGE) && ((age > tree -> maxTreeAge) || (tree -> maxTreeAge == NO_AGE)))
   tree -> maxTreeAge = age;
 else if (changeAll != TRUE) return TRUE;
 for (i = 0; i < tree -> nodeArray -> len; ++ i) {
   navNode = (GNode*) g_ptr_array_index (tree -> nodeArray, i);
   actNode = (PHYLONODE*) navNode -> data;
   if (actNode -> minAge != NO_AGE)
     actNode -> ageX = tree -> maxTreeAge - actNode -> minAge;
   else
     actNode -> ageX = tree -> maxTreeAge;
 }
 return TRUE;
}

SCOUNTER GetNumTermsWithGeo () {
 SCOUNTER num;
 GList* nav;
 TERMINAL* actTerm;

 for (nav = GETSPP, num = 0; nav != NULL; nav = g_list_next (nav)) {
   actTerm = (TERMINAL*) nav -> data;
   if (actTerm -> numGeo > 0)
     ++ num;
 }
 return num;
}

void UpdateWithData (TERMINAL* actTerm) {
 GNode* navNode;
 PHYLONODE* actNode;

 navNode = actTerm -> node;
 actNode = (PHYLONODE*) navNode -> data;
 if ((actTerm -> numGeo > 0) && (actNode -> withData == 0)) {
   for (; navNode != NULL; navNode = navNode -> parent) {
     actNode = (PHYLONODE*) navNode -> data;
     ++ actNode -> withData;
   }
 }
 else if ((actTerm -> numGeo == 0) && (actNode -> withData == 1))  {
   for (; navNode != NULL; navNode = navNode -> parent) {
     actNode = (PHYLONODE*) navNode -> data;
     ++ actNode -> withData;
   }
 }
}

void PutFillValue (PHYLONODE* node, SCOUNTER fillValue) {
 GNode* navDesc;
 GNode* actNode;

 node -> fill = fillValue;
 actNode = node -> node;
 for (navDesc = g_node_first_child (actNode); navDesc != NULL; navDesc = g_node_next_sibling (navDesc))
   PutFillValue ((PHYLONODE*) navDesc -> data, fillValue);
}
